using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;

[CustomEditor(typeof(QuadTree))]
public class QuadTreeUtil : Editor
{
    private QuadTree tree;
    private bool isDirty = false;
    private int cubeNum = 100;

    private Point minPoint;
    private Point maxPoint;
    
    private List<Point> points;
    private List<Square> squares;

    /// <summary>
    /// 测试开关
    /// </summary>
    private bool isTest;

    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();
        
        testDraw();
        testGroup();
    }

    private void OnSceneGUI()
    {
        // 获取当前编辑器操作事件
        Event guiEvent = Event.current;
        if (guiEvent.type == EventType.Repaint)
        {
            draw();
        }
        else if (guiEvent.type == EventType.Layout)
        {
            // 切换到激活对象，响应
            HandleUtility.AddDefaultControl(GUIUtility.GetControlID(FocusType.Passive));
        }
        else
        {
            handleInput(guiEvent);
            
            if (isDirty)
                HandleUtility.Repaint();
        }
    }

    /// <summary>
    /// 重新绘制
    /// </summary>
    private void draw()
    {
        drawPivot();

        // for test
        if (player != null)
            player.Draw();
        
        drawPoints();
        drawSuqare();
        // Debug.Log("Draw");
        isDirty = false;
    }

    // 绘制定位点
    private void drawPivot()
    {
        var pivotColor = QuadSetting.Instance.PivotColor;
        minPoint.color = pivotColor;
        maxPoint.color = pivotColor;
        var pr = QuadSetting.Instance.pivotR;
        minPoint.radius = pr;
        maxPoint.radius = pr;

        minPoint.pos = tree.MinPoint;
        maxPoint.pos = tree.MaxPoint;
        
        minPoint.Draw();
        maxPoint.Draw();
    }

    private void drawPoints()
    {
        // draw test points;
        if (points == null)
            points = new List<Point>();
        
        var cache = isTest ? tree.ShowCache : tree.Cache;
        for (int i = 0; i < cache.Count; i++)
        {
            if (i < points.Count)
            {
                if (points[i].pos != cache[i])
                {
                    points[i].pos = cache[i];
                }
                
                points[i].Draw();
            }
            else
            {
                var p = new Point();
                p.pos = cache[i];
                p.radius = QuadSetting.Instance.TestPointRadius;
                p.color = QuadSetting.Instance.TestPointColor;
                points.Add(p);
                p.Draw();
            }
        }
    }

    // 绘制支线
    private void drawSuqare()
    {
        // draw pivot cube
        if (squares == null)
            squares = new List<Square>();
        squares.Clear();
        
        var sq = new Square();
        sq.center = tree.transform.position;
        sq.width = maxPoint.pos.x - minPoint.pos.x;
        sq.height = maxPoint.pos.y - minPoint.pos.y;
        sq.color = QuadSetting.Instance.DefaultLineColor;
        squares.Add(sq);
        
        if (!isTest)
            drawNode(tree.root); // draw tree node square
        else
            drawShowNode();
        
        for (var i = 0; i < squares.Count; i++)
            squares[i].Draw();
    }
    
    /// <summary>
    /// 绘制检测范围内区域
    /// </summary>
    private void drawShowNode()
    {
        if (tree == null)
            return;
        
        tree.ShowNodes.ForEach(node =>
        {
            var sq = new Square();
            sq.center = node.center;
            sq.width = node.maxP.x - node.minP.x;
            sq.height = node.maxP.y - node.minP.y;
            sq.color = QuadSetting.Instance.DefaultLineColor;
            squares.Add(sq);
        });
    }

    private void drawNode(Node node)
    {
        if (node == null)
            return;
        
        var sq = new Square();
        sq.center = node.center;
        sq.width = node.maxP.x - node.minP.x;
        sq.height = node.maxP.y - node.minP.y;
        sq.color = QuadSetting.Instance.DefaultLineColor;
        squares.Add(sq);

        if (node.leftdown != null)
            drawNode(node.leftdown);
        
        if (node.leftup != null)
            drawNode(node.leftup);
        
        if (node.rightdown != null)
            drawNode(node.rightdown);
        
        if (node.rightup != null)
            drawNode(node.rightup);
    }

    /// <summary>
    /// 响应鼠标事件
    /// </summary>
    private void handleInput(Event guiEvent)
    {
        float drawPlaneHeight = 0;
        Ray mouseRay = HandleUtility.GUIPointToWorldRay(guiEvent.mousePosition);
        float dstToDrawPlane = (drawPlaneHeight - mouseRay.origin.z) / mouseRay.direction.z;
        Vector3 pos = mouseRay.GetPoint(dstToDrawPlane);

        if (guiEvent.type == EventType.MouseDown)
        {
            foreach (var point in Point.Caches)
            {
                point.OnMouseDown?.Invoke(guiEvent, pos);
            }
            
            isDirty = true;
        }
        else if (guiEvent.type == EventType.MouseUp)
        {
            foreach (var point in Point.Caches)
            {
                point.OnMouseUp?.Invoke(guiEvent, pos);
            }
            
            isDirty = true;
        }
        else if (guiEvent.type == EventType.MouseDrag)
        {
            foreach (var point in Point.Caches)
            {
                point.OnMouseDrag?.Invoke(guiEvent, pos);
                // Debug.Log("Drag--");
            }
            
            isDirty = true;
        }
        else if (guiEvent.type == EventType.MouseMove)
        {
            foreach (var point in Point.Caches)
            {
                // Debug.Log("pointPos: " + point.pos + "pos: " + pos);
                // Debug.Log("<color=\"00ff00\">dis: " + Vector3.Distance(point.pos, pos));
                if (Vector3.Distance(point.pos, pos) < point.radius)
                {
                    point.OnMouseIn?.Invoke();
                }
                else
                {
                    point.OnMouseOut?.Invoke();
                }
            }
        }
    }
    
    /// <summary>
    /// 测试地图内容绘制
    /// </summary>
    private void testDraw()
    {
        randomGenerate();
        refreshDraw();
        caculate();
    }
    
    /// <summary>
    /// 是否开始测试
    /// </summary>
    private DragArea player;
    private void testGroup()
    {
        EditorGUI.BeginChangeCheck();
        isTest = GUILayout.Toggle(isTest, "是否测试");
        if (EditorGUI.EndChangeCheck())
        {
            if (!isTest)
                player = null;
            else
            {
                player = new DragArea(Vector3.zero, QuadSetting.Instance.VisibleSize);
                player.Init();
                player.tree = tree;
            }
        }
    }

    // 创建随机对象
    void randomGenerate()
    { 
        EditorGUILayout.BeginHorizontal();

        EditorGUI.BeginChangeCheck();
        var r = GUILayout.TextField(cubeNum.ToString());
        if (EditorGUI.EndChangeCheck())
        {
            Regex regex = new Regex(@"\d+");
            var e = Convert.ToInt32(regex.Match(r).Groups.Cast<Match>().First().Value);
            cubeNum = e == null ? 0 :e;
            QuadSetting.Instance.TestCount = cubeNum;
        }
        
        if (GUILayout.Button("随机创建"))
        {
            if (tree != null)
            {
                for (int i = 0; i < cubeNum; i++)
                {
                    tree.GenerateTest();
                }
                // 直接计算
                tree.StartCaculate();
                isDirty = true;
            }
        }

        if (GUILayout.Button("清理对象"))
        {
            if (tree != null)
                tree.ClearCache();
            isDirty = true;
        }

        EditorGUILayout.EndHorizontal();
    }

    private void refreshDraw()
    {
        if (GUILayout.Button("刷新"))
        {
            points.Clear();
            isDirty = true;
        }
    }

    private void caculate()
    {
        if (GUILayout.Button("开始计算"))
        {
            if (tree != null)
                tree.StartCaculate();
            
            isDirty = true;
        }
    }

    void OnEnable()
    {
        tree = target as QuadTree;
        tree.ResetSearch();
        
        minPoint = new Point();
        maxPoint = new Point();

        cubeNum = QuadSetting.Instance.TestCount;
        
        Undo.undoRedoPerformed += OnUndoOrRedo;
        Tools.hidden = true;
    }
    
    private void OnDisable()
    {
        Undo.undoRedoPerformed -= OnUndoOrRedo;
        Tools.hidden = false;

        if (Point.Caches != null)
            Point.Caches.Clear();
    }

    void OnUndoOrRedo()
    {
        
    }
}
